package com.glsc.ngateway.platform.service.costallocation;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.glsc.ngateway.common.api.common.utils.PageUtil;
import com.glsc.ngateway.common.api.ldap.dto.LdapEhrEmpDto;
import com.glsc.ngateway.common.api.platform.dto.user.PlatformUserDto;
import com.glsc.ngateway.common.base.exception.PlatformException;
import com.glsc.ngateway.platform.aspect.UserCache;
import com.glsc.ngateway.platform.domain.costallocation.*;
import com.glsc.ngateway.platform.dto.IpDetailInfoDto;
import com.glsc.ngateway.platform.dto.IpInfoDto;
import com.glsc.ngateway.platform.dto.IpInfoQueryDto;
import com.glsc.ngateway.platform.repo.costallocation.*;
import com.glsc.ngateway.platform.service.UserService;
import com.glsc.ngateway.platform.utils.SpecificationUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.glsc.ngateway.common.api.common.enums.Constant.COSTALLOCATION_SYSTEMCOMPONENTDETAIL_ADMIN;

/**
 * @author guoj
 * @description: 历史应用详情明细
 * @date 2024/8/21 11:06
 */
@Service
public class HisIpInfoService {
    private static Logger logger = LoggerFactory.getLogger(HisIpInfoService.class);
    @Autowired
    private UserCache userCache;
    @Autowired
    private HttpServletRequest request;
    @Autowired
    private SystemComponentCategoryRepo systemComponentCategoryRepo;
    @Autowired
    private BusiSystemInfoRepo busiSystemInfoRepo;
    @Resource
    private UserService userService;
    @Autowired
    private HisIpInfoRepo hisIpInfoRepo;
    @Autowired
    private IpDetailInfoRepo ipDetailInfoRepo;
    @Autowired
    private HisIpDetailInfoRepo hisIpDetailInfoRepo;
    @Autowired
    private HisIpDetailInfoService hisIpDetailInfoService;

    /**
     * 批量保存应用详情流水
     * @param infos
     * @param infoId2Detail
     */
    public void saveHisIpInfoBatch(List<IpInfo> infos, Map<Long, List<IpDetailInfo>> infoId2Detail) {
        logger.info("开始批量保存应用详情流水大下：{}，明细大小：{}", infos.size(), infoId2Detail.size());
        PlatformUserDto user = userCache.get(request);
        String username = user.getUsername();
        for(IpInfo temp : infos){
            HisIpInfo toSave = new HisIpInfo();
            BeanUtils.copyProperties(temp, toSave);
            toSave.setCreateTime(LocalDateTime.now());
            toSave.setId(null);
            toSave.setChangeSource("应用详情");
            toSave.setOperator(username);
            HisIpInfo saved = hisIpInfoRepo.save(toSave);
            List<IpDetailInfo> ipDetailInfoDtoListDto = infoId2Detail.get(temp.getId());
            List<IpDetailInfo> ipDetailInfoDtoList = ipDetailInfoDtoListDto == null ? new ArrayList<>() : ipDetailInfoDtoListDto;
            List<HisIpDetailInfo> toSaveDetaillist = new ArrayList<>();
            for (IpDetailInfo dto : ipDetailInfoDtoList) {
                HisIpDetailInfo toSaveDetail = new HisIpDetailInfo();
                BeanUtils.copyProperties(dto, toSaveDetail);
                toSaveDetail.setId(null);
                toSaveDetail.setOperator(username);
                toSaveDetail.setIpInfoId(saved.getId());
                toSaveDetail.setCreateTime(LocalDateTime.now());
                toSaveDetaillist.add(toSaveDetail);
            }
            if (!CollectionUtils.isEmpty(toSaveDetaillist)) {
                hisIpDetailInfoRepo.saveAll(toSaveDetaillist);
            }
        }
    }

    public void saveHisIpInfo(IpInfoDto ipInfoDto, String username,String changeType) {
        HisIpInfo toSave = new HisIpInfo();
        BeanUtils.copyProperties(ipInfoDto, toSave);
        toSave.setCreateTime(LocalDateTime.now());
        toSave.setChangeSource("应用详情");
        toSave.setChangeType(changeType);
        toSave.setOperator(username);
        HisIpInfo saved = hisIpInfoRepo.save(toSave);
        List<IpDetailInfoDto> ipDetailInfoDtoListDto = ipInfoDto.getIpDetailInfoList();
        List<IpDetailInfoDto> ipDetailInfoDtoList = ipDetailInfoDtoListDto == null ? new ArrayList<>() : ipDetailInfoDtoListDto;
        List<HisIpDetailInfo> toSaveDetaillist = new ArrayList<>();
        for (IpDetailInfoDto dto : ipDetailInfoDtoList) {
            HisIpDetailInfo toSaveDetail = new HisIpDetailInfo();
            BeanUtils.copyProperties(dto, toSaveDetail);
            toSaveDetail.setOperator(username);
            toSaveDetail.setIpInfoId(saved.getId());
            toSaveDetail.setCreateTime(LocalDateTime.now());
            toSaveDetaillist.add(toSaveDetail);
        }
        if (!CollectionUtils.isEmpty(toSaveDetaillist)) {
            hisIpDetailInfoRepo.saveAll(toSaveDetaillist);
        }
    }

    public void saveHisIpInfoFromSync(IpInfo ipInfo, String operator,String changeType) {
        HisIpInfo toSave = new HisIpInfo();
        BeanUtils.copyProperties(ipInfo, toSave);
        toSave.setCreateTime(LocalDateTime.now());
        toSave.setId(null);
        toSave.setChangeSource("IT资产或OA流程");
        toSave.setChangeType(changeType);
        toSave.setOperator(operator);
        HisIpInfo saved = hisIpInfoRepo.save(toSave);

        List<IpDetailInfo> queryResult = ipDetailInfoRepo.queryByIpInfoIdOne(ipInfo.getId());
        List<IpDetailInfo> ipDetailInfoList = CollectionUtil.isEmpty(queryResult) ? new ArrayList<>() : queryResult;
        List<HisIpDetailInfo> toSaveDetaillist = new ArrayList<>();
        for (IpDetailInfo dto : ipDetailInfoList) {
            HisIpDetailInfo toSaveDetail = new HisIpDetailInfo();
            BeanUtils.copyProperties(dto, toSaveDetail);
            toSaveDetail.setOperator(operator);
            toSaveDetail.setId(null);
            toSaveDetail.setIpInfoId(saved.getId());
            toSaveDetail.setCreateTime(LocalDateTime.now());
            toSaveDetaillist.add(toSaveDetail);
        }
        if (!CollectionUtils.isEmpty(toSaveDetaillist)) {
            hisIpDetailInfoRepo.saveAll(toSaveDetaillist);
        }
    }

    public Page<HisIpInfo> findPage(IpInfoQueryDto ipInfoQueryDto) {
        logger.info("开始查询系统信息：{}", ipInfoQueryDto.toString());
        String pageSizeQuery = ipInfoQueryDto.getPageSize();
        String pageNoQuery = ipInfoQueryDto.getPageNo();
        Integer pageNo = Objects.nonNull(pageNoQuery) ? Integer.parseInt(pageNoQuery) : null;
        Integer pageSize = Objects.nonNull(pageSizeQuery) ? Integer.parseInt(pageSizeQuery) : null;
        pageNo = PageUtil.initPageNo(pageNo);
        pageSize = PageUtil.initPageSize(pageSize);
        Sort.Direction direction = Sort.Direction.DESC;
        String sort = "id";
        Pageable pageable = PageRequest.of(pageNo - 1, pageSize, direction, sort);
        List<HisIpInfo> resultList = queryIpInfoList(ipInfoQueryDto);
        List<HisIpInfo> resultListfilled = fillingName(resultList);
        resultList.sort(new Comparator<HisIpInfo>() {
            @Override
            public int compare(HisIpInfo o1, HisIpInfo o2) {
                return o2.getId().compareTo(o1.getId());
            }
        });
        return PageUtil.listConvertToPage(resultListfilled, pageable);
    }

    public List<HisIpInfo> queryIpInfoList(IpInfoQueryDto ipInfoQueryDto) {
        Map<String, Object> param = new HashMap<>();
        PlatformUserDto user = userCache.get(request);
        Set<String> roleNames = new HashSet<>(user.getRoles());
        //个人数据 否则全部数据
        if (!"itadmin".equals(user.getUsername())
                && !roleNames.contains("itadmin")
                && !roleNames.contains(COSTALLOCATION_SYSTEMCOMPONENTDETAIL_ADMIN)) {
            param.put("LIKE_systemUserId", user.getUsername());
        }
        String ip = ipInfoQueryDto.getIp();
        if (StrUtil.isNotBlank(ip)) {
            param.put("LIKE_ip", ip);
        }
        String uniqueKey = ipInfoQueryDto.getUniqueKey();
        if (StrUtil.isNotBlank(uniqueKey)) {
            param.put("EQ_uniqueKey", uniqueKey);
        }
        String ipStatus = ipInfoQueryDto.getIpStatus();
        if (StrUtil.isNotBlank(ipStatus)) {
            param.put("EQ_ipStatus", ipStatus);
        }
        String sourceType = ipInfoQueryDto.getSourceType();
        if (StrUtil.isNotBlank(sourceType)) {
            param.put("EQ_sourceType", sourceType);
        }
        Map<String, Object> paramIpDetail = new HashMap<>();
        String systemClass = ipInfoQueryDto.getSystemClass();
        if (StrUtil.isNotEmpty(systemClass)) {
            paramIpDetail.put("EQ_systemClass", systemClass);
        }
        String mainClass = ipInfoQueryDto.getMainClass();
        if (StrUtil.isNotEmpty(mainClass)) {
            paramIpDetail.put("EQ_mainClass", mainClass);
        }
        String systemClassNo = ipInfoQueryDto.getSystemClassNo();
        if (StrUtil.isNotEmpty(systemClassNo)) {
            paramIpDetail.put("EQ_systemClassNo", systemClassNo);
        }
        String exclusive = ipInfoQueryDto.getExclusive();
        if (StrUtil.isNotEmpty(exclusive)) {
            paramIpDetail.put("EQ_exclusive", exclusive);
        }
        String componentName = ipInfoQueryDto.getComponentName();
        if (StrUtil.isNotEmpty(componentName)) {
            paramIpDetail.put("LIKE_componentName", componentName);
        }
        String componentCategory = ipInfoQueryDto.getComponentCategory();
        if (StrUtil.isNotEmpty(componentCategory)) {
            paramIpDetail.put("EQ_componentCategory", componentCategory);
        }
        String componentPath = ipInfoQueryDto.getComponentPath();
        if (StrUtil.isNotEmpty(componentPath)) {
            paramIpDetail.put("LIKE_componentPath", componentPath);
        }
        String runStatus = ipInfoQueryDto.getRunStatus();
        if (StrUtil.isNotEmpty(runStatus)) {
            paramIpDetail.put("EQ_runStatus", runStatus);
        }
        String mainOrBackup = ipInfoQueryDto.getMainOrBackup();
        if (StrUtil.isNotEmpty(mainOrBackup)) {
            paramIpDetail.put("EQ_mainOrBackup", mainOrBackup);
        }
        List<HisIpInfo> content = hisIpInfoRepo.findAll(SpecificationUtil.buildSpecification(param, HisIpInfo.class));
        List<HisIpInfo> resultList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(content)) {
            Set<Long> collect = content.stream().map(HisIpInfo::getId).collect(Collectors.toSet());
            Map<Long, List<HisIpDetailInfo>> systemid2List = hisIpDetailInfoService.queryByHisIpInfoIdSet(collect, paramIpDetail);
            for (HisIpInfo temp : content) {
                List<HisIpDetailInfo> hisIpDetailInfoList = systemid2List.get(temp.getId());
                if (CollectionUtil.isNotEmpty(hisIpDetailInfoList)) {
                    temp.setHisIpDetailInfoList(hisIpDetailInfoList);
                    resultList.add(temp);
                } else {
                    if (paramIpDetail.size() == 0) {
                        temp.setHisIpDetailInfoList(new ArrayList<>());
                        resultList.add(temp);
                    }
                }
            }
        }
        return resultList;
    }

    /**
     * 填充系统和应用类别的名称
     *
     * @param resultList
     * @return
     */
    private List<HisIpInfo> fillingName(List<HisIpInfo> resultList) {
        if (CollectionUtil.isEmpty(resultList)) {
            return resultList;
        }
        Set<Long> componentCategoryIdSet = new HashSet<>();
        Set<String> systemClassNoSet = new HashSet<>();
        for (HisIpInfo temp : resultList) {
            List<HisIpDetailInfo> hisIpDetailInfoList = temp.getHisIpDetailInfoList();
            Set<Long> componentCategoryIdSetTemp = hisIpDetailInfoList.stream()
                    .filter(x -> Objects.nonNull(x.getComponentCategoryId()))
                    .map(HisIpDetailInfo::getComponentCategoryId)
                    .collect(Collectors.toSet());
            componentCategoryIdSet.addAll(componentCategoryIdSetTemp);
            Set<String> systemClassNoSetTemp = hisIpDetailInfoList.stream()
                    .filter(x -> Objects.nonNull(x.getSystemClassNo()))
                    .map(HisIpDetailInfo::getSystemClassNo)
                    .collect(Collectors.toSet());
            systemClassNoSet.addAll(systemClassNoSetTemp);
        }
        Map<Long, String> id2Name = new HashMap<>();
        if (CollectionUtil.isNotEmpty(componentCategoryIdSet)) {
            List<SystemComponentCategory> sccList = systemComponentCategoryRepo.queryByIdSet(componentCategoryIdSet);
            id2Name = sccList.stream().collect(Collectors.toMap(SystemComponentCategory::getId, SystemComponentCategory::getComponentCategory));
        }
        Map<String, String> systemClassNo2Name = new HashMap<>();
        if (CollectionUtil.isNotEmpty(systemClassNoSet)) {
            List<BusiSystemInfo> busiSystemInfos = busiSystemInfoRepo.queryBySystemClassNo(systemClassNoSet);
            systemClassNo2Name = busiSystemInfos.stream().collect(Collectors.toMap(BusiSystemInfo::getSystemClassNo, BusiSystemInfo::getSystemName));
        }

        for(HisIpInfo temp : resultList){
            List<HisIpDetailInfo> ipDetailInfoList = temp.getHisIpDetailInfoList();
            List<HisIpDetailInfo> toreturn = new ArrayList<>();
            for(HisIpDetailInfo ipTemp : ipDetailInfoList){
                HisIpDetailInfo ipDetailInfo = new HisIpDetailInfo();
                //避免JPA自动更新
                BeanUtils.copyProperties(ipTemp,ipDetailInfo);
                Long componentCategoryId = ipTemp.getComponentCategoryId();
                //历史表中已保存数据
                String componentCategory = ipDetailInfo.getComponentCategory();
                if(StrUtil.isBlank(componentCategory)){
                    ipDetailInfo.setComponentCategory(id2Name.get(componentCategoryId));
                }
                String systemClassNo = ipTemp.getSystemClassNo();
                ipDetailInfo.setSystemName(systemClassNo2Name.get(systemClassNo));
                toreturn.add(ipDetailInfo);
            }
            temp.setHisIpDetailInfoList(toreturn);
        }

        return resultList;
    }

    public Map<String,Object> compareDifferentPoint(Long leftId, Long rightId) {
        if (leftId.compareTo(rightId) == 0) {
            throw PlatformException.error("请选择两笔不同流水");
        }
        //左对应变更前  右对应变更后
        //要求left < right
        if (leftId.compareTo(rightId) > 0) {
            Long temp = leftId;
            leftId = rightId;
            rightId = temp;
        }
        Set<Long> ids = new HashSet<>();
        ids.add(leftId);
        ids.add(rightId);
        List<HisIpInfo> hisIpInfos = hisIpInfoRepo.queryByids(ids);
        if (CollectionUtil.isEmpty(hisIpInfos) || hisIpInfos.size() != 2) {
            throw PlatformException.error("未找到相关流水");
        }
        List<HisIpDetailInfo> hisIpDetailInfos = hisIpDetailInfoRepo.queryByHisIpInfoIds(ids);
        Map<Long, List<HisIpDetailInfo>> infoid2List = hisIpDetailInfos.stream()
                .collect(Collectors.groupingBy(HisIpDetailInfo::getIpInfoId));
        Map<String, Object> result = new HashMap<>();
        HisIpInfo left = new HisIpInfo();
        HisIpInfo right = new HisIpInfo();
        Set<String> sytemUserOA= hisIpInfos.stream().map(HisIpInfo::getSystemUserId).collect(Collectors.toSet());
        Set<String> OperatorOA = hisIpInfos.stream().map(HisIpInfo::getOperator).collect(Collectors.toSet());
        sytemUserOA.addAll(OperatorOA);
        List<LdapEhrEmpDto> usersFromEhr = userService.getUsersFromEhr(new ArrayList<>(sytemUserOA));
        Map<String, String> oa2name = usersFromEhr.stream()
                .collect(Collectors.toMap(LdapEhrEmpDto::getBadge, LdapEhrEmpDto::getName));
        for (HisIpInfo temp : hisIpInfos) {
            Long id = temp.getId();
            List<HisIpDetailInfo> detailTemp = infoid2List.get(id);
            if (leftId.compareTo(id) == 0) {
                BeanUtils.copyProperties(temp,left);
                String systemUserId = left.getSystemUserId();
                String operator = left.getOperator();
                left.setSystemUserName(oa2name.get(systemUserId));
                left.setOperatorName(oa2name.get(operator));
                left.setHisIpDetailInfoList(detailTemp);
                result.put("left", left);
            }
            if (rightId.compareTo(id) == 0) {
                BeanUtils.copyProperties(temp,right);
                String systemUserId = right.getSystemUserId();
                String operator = right.getOperator();
                right.setSystemUserName(oa2name.get(systemUserId));
                right.setOperatorName(oa2name.get(operator));
                right.setHisIpDetailInfoList(detailTemp);
                result.put("right", right);
            }
        }
        String[] ignoreField = {"id","currIp","oldIp","changeStatus","hisIpDetailInfoList"};
        Map<String, List<Object>> stringListMap = compareFields(right, left,ignoreField);
        result.put("diff", stringListMap.keySet());
        return result;
    }




    /**
     *
     * <p>Description:  比较两个实体属性值
     * @param obj1
     * @param obj2
     * @param ignoreArr 需要忽略的字段
     * @return
     */
    public static Map<String, List<Object>> compareFields(Object obj1, Object obj2, String[] ignoreArr) {
        try{
            Map<String, List<Object>> map = new HashMap<String, List<Object>>();
            List<String> ignoreList = null;
            if(ignoreArr != null && ignoreArr.length > 0){
                // array转化为list
                ignoreList = Arrays.asList(ignoreArr);
            }
            if (obj1.getClass() == obj2.getClass()) {// 只有两个对象都是同一类型的才有可比性
                Class clazz = obj1.getClass();
                // 获取object的属性描述
                PropertyDescriptor[] pds = Introspector.getBeanInfo(clazz,
                        Object.class).getPropertyDescriptors();
                for (PropertyDescriptor pd : pds) {// 这里就是所有的属性了
                    String name = pd.getName();// 属性名
                    if(ignoreList != null && ignoreList.contains(name)){// 如果当前属性选择忽略比较，跳到下一次循环
                        continue;
                    }
                    Method readMethod = pd.getReadMethod();// get方法
                    // 在obj1上调用get方法等同于获得obj1的属性值
                    Object o1 = readMethod.invoke(obj1);
                    // 在obj2上调用get方法等同于获得obj2的属性值
                    Object o2 = readMethod.invoke(obj2);
                    if(o1 == null && o2 == null){                               //o1 旧     o2新    如果两个都是空就跳过
                        continue;
                    }else if(o1 == null && o2 != null){                         //如果O1旧的没有     O2新的有
                        List<Object> list = new ArrayList<Object>();
                        list.add("旧(null)");
                        list.add("新"+"("+String.valueOf(o2)+")");
                        map.put(name, list);
                        continue;
                    }
                    if (!o1.equals(o2)) {// 比较这两个值是否相等,不等就可以放入map了                             //如果旧的和新的不相等
                        List<Object> list = new ArrayList<Object>();

                        if(o2==null) {
                            list.add("旧"+"("+String.valueOf(o1)+")");
                            list.add("新()");
                        }else {
                            list.add("旧"+"("+String.valueOf(o1)+")");
                            list.add("新"+"("+String.valueOf(o2)+")");
                        }
                        map.put(name, list);
                    }
                }
            }
            return map;
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }
    }

    @Transactional
    public void delete(Long id){
        logger.info("删除应用详情流水：{}", id);
        HisIpInfo hisIpInfo = hisIpInfoRepo.queryByid(id);
        if (hisIpInfo == null) {
            throw  PlatformException.errorWithOutMail("未找到流水记录");
        }
        Long id1 = hisIpInfo.getId();
        List<HisIpDetailInfo> detailInfos = hisIpDetailInfoRepo.queryByHisIpInfoId(id1);
        if(CollectionUtil.isNotEmpty(detailInfos)){
            hisIpDetailInfoRepo.deleteAll(detailInfos);
        }
        hisIpInfoRepo.delete(hisIpInfo);
    }

}
